﻿#include<stdio.h>
int main()
{
	int a[] = { 1,2,3,4 };
	printf("%zu\n", sizeof(a));// 4 * 4 = 16
	//a是数组名，数组名单独放在sizeof中，数组名表示整个数组，计算的是整个数组的大小，单位是字节
	printf("%zu\n", sizeof(a + 0));// 4/8(X64与X86环境不同)
	//a是数组名，但是并没有单独放在sizeof内部，也没有&，不符合俩种特殊情况，所以a就是首元素的地址
	//a + 0，还是首元素的地址，sizeof(a + 0)计算的是一个地址的大小，即为4/8(X64与X86环境不同)
	printf("%zu\n", sizeof(*a));// 4
	//不是特殊情况，a就相当于&a[0]，*a就相当于*&a[0],就是首元素(整数1)，所以sizeof(*a)就是四个字节
	printf("%zu\n", sizeof(a + 1));// 4/8(X64与X86环境不同)
	//不是特殊情况，a就是首元素的地址，a + 1就是第二个元素的地址，sizeof(a + 1)计算的是地址的大小
	printf("%zu\n", sizeof(a[1]));// 4
	//a[1]就是数组的第二个元素，大小是四个字节，只有地址才是4/8个字节
	printf("%zu\n", sizeof(&a));// 4/8(X64与X86环境不同)
	//特殊情况取出的是整个数组的地址，数组的地址也是地址，只要是地址大小就是四个或者八个字节
	//&的特殊性，体现在+ -整数这一块，int(*p)[4] = &a
	printf("%zu\n", sizeof(*&a));// 16
	//*&a == a 就变成了与第一个相同的了
	//&a取出的是数组的地址，它的类型是int(*)[4],对于数组指针解引用，访问的是这个数组，大小就是整个数组
	printf("%zu\n", sizeof(&a + 1));// 4/8(X64与X86环境不同)
	//&a是数组的地址，&a + 1就是跳过整个数组后的地址，是地址大小就是四个或者八个字节
	printf("%zu\n", sizeof(&a[0]));// 4/8(X64与X86环境不同)
	//&a[0]就是第一个元素的地址，是地址大小就是四个或者八个字节
	printf("%zu\n", sizeof(&a[0] + 1));// 4/8(X64与X86环境不同)
	//&a[0] + 1就是数组第二个元素的地址，是地址大小就是四个或者八个字节
	return 0;
}
#include <stdio.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%zu\n", sizeof(arr));// 1 * 6 = 6
	//特殊情况，arr表示整个数组，sizeof(arr)计算的就是整个数组的大小
	printf("%zu\n", sizeof(arr + 0));// 4/8(X64与X86环境不同)
	//非特殊情况，arr表示数组首元素的地址，只要是地址，大小就是四个或者八个字节
	printf("%zu\n", sizeof(*arr));// 1 * 1 = 1
	//非特殊情况，arr表示数组首元素的地址，所以*arr就表示首元素，sizeof(*arr)计算的是首元素的大小
	printf("%zu\n", sizeof(arr[1]));// 1 * 1 = 1
	//arr[1]就是第二个元素，大小就是一个字节
	printf("%zu\n", sizeof(&arr));// 4/8(X64与X86环境不同)
	//特殊情况，&arr取出的是数组的地址，只要是地址，大小就是四个或者八个字节
	printf("%zu\n", sizeof(&arr + 1));// 4/8(X64与X86环境不同)
	//&arr + 1是跳过整个数组后的地址，跳过之后依旧是地址，是地址大小就是四个或者八个字节
	printf("%zu\n", sizeof(&arr[0] + 1));// 4/8(X64与X86环境不同)
	//&arr[0] + 1就是第二个元素的地址，只要是地址，大小就是四个或者八个字节
	return 0;
}
#include <stdio.h>
#include <string.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	//要注意strlen只能求字符型
	printf("%zu\n", strlen(arr));// 随机值
	//非特殊情况arr表示数组首元素的地址，字符串中没有\0
	printf("%zu\n", strlen(arr + 0));// 随机值(和第一个随机值相同)
	//arr依旧是数组首元素的地址，再+0还是首元素的地址无\0
	printf("%zu\n", strlen(*arr));// 程序崩溃
	//*arr表示数组的首元素(a)，a的ASCLL码值是97，非法访问内存
	//我们应该传入的是地址，这里传入的是一个元素，不符合！！！
	printf("%zu\n", strlen(arr[1]));// 程序崩溃
	//我们应该传入的是地址，这里传入的是一个元素，非法访问内存
	printf("%zu\n", strlen(&arr));// 随机值(和第一个随机值是一样的)
	//&arr是数组的地址，从数组的起始位置开始向后数字符串的长度，要找\0，但是没有
	printf("%zu\n", strlen(&arr + 1));// 随机值
	//这个随机值应该与前面的随机值是有差异的(比第一个随机值小6)
	printf("%zu\n", strlen(&arr[0] + 1));// 随机值
	//&arr[0] + 1就是第二个元素的地址，依旧是随机值但是应该比第一个的随机值小1
	return 0;
}
#include <stdio.h>
int main()
{
	char arr[] = "abcdef";
	printf("%zu\n", sizeof(arr));// 1 * 7 = 7
	//特殊情况，arr表示整个数组的大小，注意别漏掉\0共七个元素
	printf("%zu\n", sizeof(arr + 0));// 4/8(X64与X86环境不同)
	//非特殊情况，arr表示数组首元素的地址，arr + 0依旧是数组首元素的地址
	printf("%zu\n", sizeof(*arr));// 1 * 1 = 1
	//表示第一个元素即a，所以就是计算a的大小
	printf("%zu\n", sizeof(arr[1]));// 1 * 1 = 1
	//表示第二个元素即b，所以就是计算b的大小
	printf("%zu\n", sizeof(&arr));// 4/8(X64与X86环境不同)
	//特殊情况，表示整个数组的大小
	printf("%zu\n", sizeof(&arr + 1));// 4/8(X64与X86环境不同)
	//&arr + 1表示跳过整个数组地址之后的地址
	printf("%zu\n", sizeof(&arr[0] + 1));// 4/8(X64与X86环境不同)
	//表示第二个元素的地址
	return 0;
}
#include <stdio.h>
#include <string.h>
int main()
{
	char arr[] = "abcdef";
	printf("%zu\n", strlen(arr));// 1 * 6 = 6
	//strlen表示首元素的地址，strlen找的是\0之前的字符个数
	printf("%zu\n", strlen(arr + 0));// 1 * 6 = 6
	//数组名表示首元素的地址
	printf("%zu\n", strlen(*arr));// 程序崩溃
	//传入的是一个元素不是地址，会造成非法访问
	printf("%zu\n", strlen(arr[1]));// 程序崩溃
	//传入的是一个元素不是地址，会造成非法访问
	printf("%zu\n", strlen(&arr));// 1 * 6 = 6
	//传入的是整个数组的地址，但是地址我们是从第一个元素开始数的
	printf("%zu\n", strlen(&arr + 1));// 随机值
	//跳过了整个数组的地址，我们不知道下一行\0会在哪出现
	printf("%zu\n", strlen(&arr[0] + 1));// 5
	//&arr[0] + 1表示第二个元素，所以strlen统计的是\0之前到第二个元素之间的元素个数
	return 0;
}
#include <stdio.h>
int main()
{
	char* p = "abcdef";
	printf("%zu\n", sizeof(p));// 4/8(X64与X86环境不同)
	//p是一个指针变量，里面存放的是a的地址
	printf("%zu\n", sizeof(p + 1));// 4/8(X64与X86环境不同)
	//p + 1就是b的地址
	printf("%zu\n", sizeof(*p));// 1
	//p里面存放的是a的地址，*p即为a这个元素，即为计算a的大小
	printf("%zu\n", sizeof(p[0]));// 1
	//p[0] = *(p + 0) = *p 
	printf("%zu\n", sizeof(&p));// 4/8(X64与X86环境不同)
	//&p是指针变量p的地址，是个二级指针，但本质上还是地址
	printf("%zu\n", sizeof(&p + 1));// 4/8(X64与X86环境不同)
	//如图所示
	printf("%zu\n", sizeof(&p[0] + 1));// 4/8(X64与X86环境不同)
	//&p[0] + 1是b的地址
	return 0;
}
#include <stdio.h>
#include <string.h>
int main()
{
	char* p = "abcdef";
	printf("%zu\n", strlen(p));// 6
	//传入的是a的地址，相当于从a开始往后数
	printf("%zu\n", strlen(p + 1));// 5 
	//传入的是b的地址
	printf("%zu\n", strlen(*p));// 程序崩溃
	//传入的不是地址
	printf("%zu\n", strlen(p[0]));// 程序崩溃
	//传入的不是地址
	printf("%zu\n", strlen(&p));// 随机值
	//这是地址的地址，p的内容不确定，p变量在内存中后续的空间布局不确定
	printf("%zu\n", strlen(&p + 1));// 随机值
	//与上面的俩者间毫无联系
	printf("%zu\n", strlen(&p[0] + 1));// 5
	//传入的是b的地址
	return 0;
}
#include <stdio.h>
int main()
{
	int a[3][4] = { 0 };
	printf("%zu\n", sizeof(a));// 48
	//a单独放在sizeof中表示整个数组的大小:4 * 3 * 4 == 48 
	printf("%zu\n", sizeof(a[0][0]));// 4
	//表示第一行第一个元素的大小
	printf("%zu\n", sizeof(a[0]));// 16
	//a[0]是第一行这个一维数组的数组名，单独放在sizeof内部了，所以计算的是a[0]整个的大小
	printf("%zu\n", sizeof(a[0] + 1));// 4/8(X64与X86环境不同)
	//非特殊情况，所以a[0]表示首元素的地址即&a[0][0]，a[0] + 1就表示第一行第二个元素的地址
	printf("%zu\n", sizeof(*(a[0] + 1)));// 4
	//a[0] + 1就表示第一行第二个元素的地址，再解引用就表示一个元素，所及计算的就是一个元素的大小
	printf("%zu\n", sizeof(a + 1));// 4/8(X64与X86环境不同)
	//非特殊情况，表示第一行的地址+1，就是第二行的地址
	//a --> int(*)[4]
	printf("%zu\n", sizeof(*(a + 1)));// 16
	//*(a + 1) == a[1]，就是第二行的数组名，符合了数组名单独放在sizeof内部中所以计算的就是整个数组的大小
	printf("%zu\n", sizeof(&a[0] + 1));// 4/8(X64与X86环境不同)
	//&a[0]是第一行的地址，取出的是第一行的地址，&a[0] + 1就是第二行的地址
	printf("%zu\n", sizeof(*(&a[0] + 1)));// 16
	//在上一个的基础上表示计算第二行数组的大小
	printf("%zu\n", sizeof(*a));// 16
	//数组名表示数组首元素的地址，*a就是第一行的大小
	printf("%zu\n", sizeof(a[3]));// 16
	// sizeof在计算变量，数组大小的时候，是通过类型来推导的，不会真实计算
	//我们就假设如果这里存放的是第四行并且计算(因为实际上不会真实计算)
	return 0;
}
#include <stdio.h>
int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int* ptr = (int*)(&a + 1);
	printf("%d,%d", *(a + 1), *(ptr - 1));
	return 0;
}
 
 
 
 
 
程序的结果是什么？
在X86环境下 //假设结构体的⼤⼩是20个字节 
//程序输出的结果是啥？ 
#include<stdio.h>
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p = (struct Test*)0x100000;//将p强制类型转换成了一个结构体指针类型
int main()
//在X86环境下整型指针是四个字节
{
	//指针+-整数
	//结构体指针 + 1就表示跳过一个结构体，题中告诉我们假设结构体的大小是20个字节
	//0x100000 + 20 == 0x100014(转换为16进制)
	printf("%p\n", p + 0x1);
	//p从结构体指针类型强制类型转换为无符号整型就是在他的基础上+1
	//0x100000 + 1 == 0x100001
	printf("%p\n", (unsigned long)p + 0x1);
	//p从结构体指针类型强制类型转换为无符号整型指针，他+1就是跳过4个字节
	//0x100000 + 4 == 0x100004
	printf("%p\n", (unsigned int*)p + 0x1);
	//注意%p打印的时候会省略0x
	return 0;
}
#include <stdio.h>
int main()
{
	//这里是逗号表达式，即我们取后面的一个元素
	//(0,1) == 1   (2,3) == 3   (4,5) == 5
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
	int* p;
	//非特殊情况，即为首元素地址
	p = a[0];//p=&a[0][0]
	printf("%d", p[0]);//p[0] = *(p + 0)
	return 0;
}
#include <stdio.h>
int main()
{
	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	//&aa是取出整个数组的地址，但是可以用首元素进行表示，&aa + 1就表示跳过整个数组
	int* ptr1 = (int*)(&aa + 1);
	//aa实际上就是&aa[0]，是首元素的地址表示一个一维数组，第一行的地址+1就表示第二行的地址
	int* ptr2 = (int*)(*(aa + 1));
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
	return 0;
}
假设环境是x86环境，程序输出的结果是啥？ 
#include <stdio.h>
int main()
{
	//p与a均为指针，一个指向4个元素，一个指向5个元素
	int a[5][5];
	int(*p)[4];
	p = a;//p = &a[0] //a也是一个指针可以写成int(*a)[5]
    printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
	//%d:按照十进制的形式，打印有符号的整数，%d认为内存中存放的是有符号整数的补码
	//%p:打印地址(16进制)，%p认为内存中存放的补码就是地址，打印时是原码
	//原码:10000000 00000000 00000000 00000100
	//取反，+1
	//补码:11111111 11111111 11111111 11111100
	//转换成十六进制
	//     FF       FF       FF       FC
    return 0;
}
//字符串逆序
#include<stdio.h>
#include<string.h>

void Reverse(char* str)//接受的是&arr[0]
{
	int len = strlen(str);
	char* left = str;//&str[0]
	char* right = str + len - 1;
	while (left < right)
	{
		char tap = *left;
		*left = *right;
		*right = tap;
		left++;
		right--;
	}
}
int main()
{
	char arr[10000] = { 0 };
	//这里不能用scanf输入因为scanf遇到空格结束
	//所以字符串的输入我们用 gets
	while (gets(arr))
	{
		Reverse(arr);//其实传过去的就是&arr[0]
		printf("%s\n", arr);
	}
	return 0;
}
//字符串左旋
#include<stdio.h>
#include<string.h>
void leftfound(char* str, int k)
{
	int len = strlen(str);
	int time = k % len;//实际左旋次数
	int i = 0;//次数
	int j = 0;//下标
	for (i = 0; i < time; i++)
	{
		char tap = str[0];
		for (j = 0; j < len - 1; j++)
		{
			str[j] = str[j + 1];
		}
		str[j] = tap;//最后一次给前面的值赋到最后
	}
}
int main()
{
	char arr[] = "hello world";
	int a = 0;
	scanf_s("%d", &a);
	leftfound(arr, a);//这里的a表示我们想左旋几个字符串
	printf("%s\n", arr);
	return 0;
}
#include<stdio.h>
#include<string.h>
int main()
{
	char str = "abcdef";
	char copy[256] = { 0 };
	strcpy(copy, str);//字符串拷贝，将str中的字符串拷贝到copy中
	printf("%s\n", copy);

	//字符串拼接
	char str2[] = "abc";
	char cat[256] = "pok";
	strcat(cat, str2);//字符串拼接，将str2拼接到cat的后面
	strncat(cat, str2, 2);//从str2指向的位置开始 拼接2个字符到cat的最后
	printf("%s\n", cat);
	return 0;
}
#include<stdio.h>
#include<string.h>
void leftfound(char* str, int k)
{
	char tap[256] = { 0 };
	int len = strlen(str);
	int time = k % len;//实际左旋次数
	strcpy(tap, str + time);
	strncat(tap, str, time);

	strcpy(str, tap);
}
int main()
{
	char arr[] = "hello world";
	int a = 0;
	scanf_s("%d", &a);
	leftfound(arr, a);//这里的a表示我们想左旋几个字符串
	printf("%s\n", arr);
	return 0;
}
#include <stdio.h>
// 函数用于调整数组，使奇数位于偶数前面
void func(int arr[], int n) 
{
    // 左指针，从数组起始位置开始
    int left = 0;
    // 右指针，从数组末尾位置开始
    int right = n - 1;
    while (left < right) 
    {
        // 找到左边的偶数
        while (left < right && arr[left] % 2 != 0)
        {
            left++;
        }
        // 找到右边的奇数
        while (left < right && arr[right] % 2 == 0) 
        {
            right--;
        }
        // 交换左右指针指向的元素
        if (left < right)
        {
            int temp = arr[left];
            arr[left] = arr[right];
            arr[right] = temp;
        }
    }
}
int main() {
    int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    int n = sizeof(arr) / sizeof(arr[0]);
    printf("原数组：");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    // 调用函数调整数组
    func(arr, n);
    printf("调整后的数组：");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    return 0;
}
